%!Tex Program = xelatex
%\documentclass[a4paper]{article}
\documentclass[a4paper]{ctexart}
\usepackage{xltxtra}
\usepackage{listings}
%\setmainfont[Mapping=tex-text]{AR PL UMing CN:style=Light}
%\setmainfont[Mapping=tex-text]{AR PL UKai CN:style=Book}
%\setmainfont[Mapping=tex-text]{WenQuanYi Zen Hei:style=Regular}
%\setmainfont[Mapping=tex-text]{WenQuanYi Zen Hei Sharp:style=Regular}
%\setmainfont[Mapping=tex-text]{AR PL KaitiM GB:style=Regular} 
%\setmainfont[Mapping=tex-text]{AR PL SungtiL GB:style=Regular} 
%\setmainfont[Mapping=tex-text]{WenQuanYi Zen Hei Mono:style=Regular} 


\title{数学软件第三次作业}
\author{晏宇青\\数学与应用数学3220104804}
\date{\today}
\begin{document}
\maketitle
\pagestyle{empty}

\section{重定向、管道}

Shell 中一切都是文件，包括标准输入输出。比如 \verb|ls -l| 的输出结果默认是输出到 \verb|stdout|，
也就是标准输出设备，但你可以用重定向操作符将其重定向为一个文件，比如：
\begin{lstlisting}[language=sh]
ls -l > output.txt
\end{lstlisting}

这个就是把 \verb|ls -l| 的输出结果保存在 \verb|output.txt| 中。 

操作符 \verb|>| 是重写一个新文件，而 \verb|>>| 是追加在原文件末尾。
尝试一下。

我们知道除了标准输出文件 \verb|stdout|，还有一个标准错误文件 \verb|stderr|，
这个往往和 \verb|stdout| 指向同一个输出设备。但可以通过重定向分离。
一般默认的输出是标准输出文件，而 2 则值标准错误文件。比如：
\begin{lstlisting}[language=sh]
kill -HUP 1234
\end{lstlisting}
删除进程 1234, 显示
\begin{lstlisting}[language=sh]
bash: kill: (1234) - No such process
\end{lstlisting}
因为一般不会正好有这个进程号。这里全部都是标准错误文件输出的，所以如果运行
\begin{lstlisting}[language=sh]
kill -HUP 1234 > output.txt
\end{lstlisting}
则得到一个空文本文件。要
\begin{lstlisting}[language=sh]
kill -HUP 1234 2> output.txt
\end{lstlisting}
才能截获错误信息到文本文件。如果我们想同时截获标准输出和错误输出并放在一个文件里，可以
\begin{lstlisting}[language=sh]
kill -HUP 1234 >killout.txt 2>&1
\end{lstlisting}
注意，\verb|2>&1| 之间不能有空格。如果输出中有很多垃圾信息你不想看，
可以将其重定向到一个垃圾桶：
\begin{lstlisting}[language=sh]
kill -l 1234 2>/dev/null
\end{lstlisting}

既然标准输出可以重定向，标准输入自然也可以
\begin{lstlisting}[language=sh]
ls ~ -lR > output.txt
\end{lstlisting}
先搞一个大一点的输出，把它丢给 \verb|more| 做输入，功能是把输入一屏幕一屏幕输出。
\begin{lstlisting}[language=sh]
more < output.txt
\end{lstlisting}
注意这里操作符反向了。

然后这里有一件很土的事情，就是这个
\verb|output.txt| 就是一个中转站，何必一定要存下来呢？就不能直接转移过去？
也就是把一个命令的输出当作另一个命令的输入。这个当然可以，就叫管道，
操作符是 \verb|||。
\begin{lstlisting}[language=sh]
ls ~ -lR | more
\end{lstlisting}
大家可能注意到我曾经在输出中只显示有关键字的部分，用的也是管道：
\begin{lstlisting}[language=sh]
ls ~ -lR | grep usr
\end{lstlisting}
管道和重定向可以根据需要多重连接。

\section{循环过程}

Shell 提供了表示循环和分支判定的结构，如：
\begin{lstlisting}[language=sh]
for file in *
do
if grep -l figure $file
then
ls $file -l
fi
done
\end{lstlisting}
这里
\begin{lstlisting}[language=sh]
for file in *
do
...something...
done
\end{lstlisting}
是一个循环结构，\verb|file| 是一个用户定义的变量，
\verb|*| 表示当前目录下所有文件，\verb|in| 表示遍历。
所以这段命令的意思就是遍历当前目录下所有文件，对每个文件重复做
\verb|...something...| 这件事。比如：
\begin{lstlisting}[language=sh]
for fAd in *
do
echo $fAd
done
\end{lstlisting}
就是遍历当前目录，将全部文件名都打印一遍。命令 \verb|echo| 是打印输出的意思。

\section{分支过程}
然后看内层是一个分支结构：
\begin{lstlisting}[language=sh]
if [ ...condition... ]
then
...do something...
fi
\end{lstlisting}
以上是一个标准的 \verb|if ... fi| 结构。但我们用的时候没有
\verb|...condition...|，类似 C 语言，只要 \verb|...condition...| 部分不为空，
我们就认为条件是真。所以这里的意思是
\begin{lstlisting}[language=sh]
if grep -l figure $file
then
ls $file -l
fi
\end{lstlisting}
只要
\begin{lstlisting}[language=sh]
grep -l figure $file
\end{lstlisting}
过滤的结果不为空，即之前遍历到的 \verb|$file| 这些文件里存在包含 \verb|figure|
单词的，就把它的详细信息列出来。你自己可以根据需要做自由的调整，比如：
\begin{lstlisting}[language=sh]
for file in *
do
if grep -l figure $file
then
cp $file ~/Documents
fi
done
\end{lstlisting}
这是什么意思？已经具有收集功能了。

\section{通配符（wildcard expansion）}
注意到这里 \verb|*| 是一个典型的通配符，意思是全部文件；类似的通配符还有：
\begin{itemize}
\item \verb|?|，任一单个字符，比如：\verb|lec0?|，那么
  \verb|lec01|，\verb|lec02| 和 \verb|lec0k| 都匹配；
\verb|[]|，括号中任一字母。比如：\verb|lec0[abc]|，那么
\verb|lec0a|，\verb|lec0b| 和 \verb|lec0c| 都匹配；
\item \verb|[^]| 或 \verb|[!]|, 不含括号内的任一字符，比如：\verb|lec0[!12]|，
  那么 \verb|lec01| 和 \verb|lec02| 都不匹配；
\item \verb|{}|，包含大括号内任一字串，比如：\verb|lec03{.log,.pdf}| 则匹配
  \verb|lec03.pdf| 或 \verb|lec03.log|。
\end{itemize}
以上通配符可自由组合，比如：\verb|*0?.{t??,a*}|。

然后 \verb|$| 操作符主要有两个意思，要么是表示将一个变量替换成变量值，如之前的 \verb|$file|；
也可以表示对命令的替换。比如：
\begin{lstlisting}[language=sh]
grep -l crazyfish *
\end{lstlisting}
表示列出目录下所有含有 \verb|crazyfish| 字符串的文件名，那么
\begin{lstlisting}[language=sh]
ls -l $(grep -l crazyfish *)
\end{lstlisting}
就表示进一步列出这些文件的详细信息。

\section{脚本代码 (script)}

现在我们可以用连续的命令加上流程控制加上通配符实现比较复杂的功能。
这些命令可以形成一个脚本，并用 bash 去运行。具体做法是依次写下命令，
然后在第一行写上 \verb|#!/bin/bash| 表示这个脚本用哪个 shell 执行。
下面是一个完整的脚本的例子：
\begin{lstlisting}[language=sh]
#!/bin/sh
# first
# This file looks through all the files in the current
# directory for the string POSIX, and then prints the names of
# those files to the standard output.
for file in *
do
if grep -q crazyfish $file 
then
echo $file 
fi
done
exit 0
\end{lstlisting}
将它存成一个文本文件，比如叫 \verb|test.sh|，然后将其设置成可执行，
然后就可以像运行一个命令一样运行它。实际上，它构成一个组合命令，或称批处理。
这里 \verb|grep -q| 表示安静地运行，或者说，不要实际输出，因为我们这里只要求
\verb|grep| 返回是否找到这样一个状态就行。

\section{变量}

既然可以写脚本了，变量自然很重要。这里变量直接使用就可以：
\begin{lstlisting}[language=sh]
salutation=Hello
echo $salutation
\end{lstlisting}
一赋值一输出。默认就是字符串，引不引号无所谓：
\begin{lstlisting}[language=sh]
salutation="Yes Dear"
echo $salutation
\end{lstlisting}
就算是给数字，还是字符串：
\begin{lstlisting}[language=sh]
salutation=7+5
echo $salutation
\end{lstlisting}

命令 \verb|read| 可以由用户给变量赋值：
\begin{lstlisting}[language=sh]
read salutation
echo $salutation
\end{lstlisting}

从上面的过程我们可以看到，在 shell 中，双引号和不加引号都是字符串，
而单引号和 \verb|\| 才是真正的引号：
\begin{lstlisting}[language=sh]
#!/bin/sh
myvar="Hi there"
echo $myvar
echo "$myvar"
echo '$myvar'
echo \$myvar
echo Enter some text
read myvar
echo '$myvar' now equals $myvar
exit 0
\end{lstlisting}
(参见：example/scripts/quoting.sh)

\section{环境变量}

除了我们自定义的变量，shell 还内置了很多环境变量，供我们和系统交互。
\begin{itemize}
\item \verb|$HOME|，用户目录；
\item \verb|$PATH|，执行程序的路径，放在这些路径下的可执行文件能够直接被运行；
\item \verb|$PS1|，命令行提示符，普通用户是 \verb|$|，超级用户是 \verb|#|；
\item \verb|$PS2|，第二提示符，比如等待输入状态，通常是 \verb|>|；
\item \verb|$IFS|，输入参数的间隔符，一般是一个空格，也可以是 TAB 或换行；
\end{itemize}
以上都可以用 \verb|echo| 输出查看。但是
\begin{lstlisting}[language=sh]
echo $PS1
\end{lstlisting}
会得到
\begin{lstlisting}[language=sh]
${debian_chroot:+($debian_chroot)}\u@\h:\w\$
\end{lstlisting}
这里转义字符 \verb|\u| 表示用户名，\verb|\h| 表示主机名，\verb|\w| 表示当前路径。
以下环境变量专门给 scripts 使用，
\begin{itemize}
\item \verb|$0|，脚本名；
\item \verb|$#|，传递给脚本的参数个数；
\item \verb|$$|，脚本的进程编号；
\item \verb|$1|，\verb|$2|，...，传递给脚本的第 1 个，第 2 个参数，...；
\item \verb|$*|，传递给脚本的全部参数，作为一个字符串；
\item \verb|$@|，传递给脚本的全部参数，有几个参数就是几个字符串；
\end{itemize}
测试脚本(try\_var.sh)
\begin{lstlisting}[language=sh]
#!/bin/bash

echo “The program $0 is now running”
echo “The first parameter was $1”
echo “The second parameter was $2”
echo “The third parameter was $3”
echo “The all parameters were $*“

echo “The parameter list was:“
for para in $@
do
echo $para
done

exit 0
\end{lstlisting}
体会一下 \verb|@*| 和 \verb|$@| 的区别。

\section{判断条件（condition）}

实际上，正经的 \verb|if| 后面应该跟一个条件判断，条件的写法是：
\begin{lstlisting}[language=sh]
if ...condition...
\end{lstlisting}
这里 \verb|...condition...| 的有两种表达，\verb|test| 或者 \verb|[  ]|，
后者方括号两端必须各有一个空格。

\begin{table}[p!]
\caption{字符串逻辑运算关系表}
\centering
\begin{tabular}{|c|c|}
\hline
字符串比较 & 结果 \\
\hline
string1 = string2 & True 如果字符串相等 \\
\hline
string1 != string2 & True 如果字符串不想等 \\
\hline
-n string & True 如果字符串非空 \\
\hline
-z string & True 如果字符串空 \\
\hline
\end{tabular}
\end{table}

\begin{table}[p!]
  
\caption{算术逻辑运算关系表}
\centering
\begin{tabular}{|c|c|}
\hline
算术表达式 & 结果 \\
\hline
expression1 -eq expression2 & True 如果表达式值相等\\
\hline
expression1 -ne expression2 & True 如果表达式值不想等\\
\hline
expression1 -gt expression2 & True 如果 expression1 大于 expression2\\
\hline
expression1 -ge expression2 & True 如果 expression1 大于等于 expression2\\
\hline
expression1 -lt expression2 & True 如果 expression1 小于 expression2\\
\hline
expression1 -le expression2 & True 如果 expression1 小于等于 expression2\\
\hline
! expression & True 如果 expression false, 或反之\\
\hline
\end{tabular}
\end{table}

\begin{table}[p!]
  \caption{文件逻辑运算关系表}
\centering
\begin{tabular}{|c|c|}
\hline
文件判断 & 结果 \\
\hline
-d file & True 如果 file 是目录\\
\hline
-e file & True 如果 file 存在\\
\hline
-f file & True 如果 file 是正经的文件\\
\hline
-g file & True 如果 file 设置了 group id\\
\hline
-r file & True 如果 file 可读\\
\hline
-s file & True 如果 file 大小不是零\\
\hline
-u file & True 如果 file 设置了 user id\\
\hline
-w file & True 如果 file 可写\\
\hline
-x file & True 如果 file 可执行\\
\hline
\end{tabular}
\end{table}

\newpage

\section{控制结构}
shell有很多控制结构，与其它编程语言类似

\verb|if|语句非常简单：它测试命令的结果，然后有条件地执行一组语句：
\begin{lstlisting}[language=sh]
if condition
then
    statements
else
    statements
fi
\end{lstlisting}
if的一个常见用法是提出一个问题，然后根据答案做出决定：
\begin{lstlisting}[language=sh]
#!/bin/sh

echo “Is it morning? Please answer yes or no”
read timeofday

if [ $timeofday = “yes” ]; then
    echo “Good morning”
else
    echo “Good afternoon”
fi

exit 0
\end{lstlisting}
这将给出以下输出：
\begin{lstlisting}[language=sh]
Is it morning? Please answer yes or no
yes
Good morning
$
\end{lstlisting}
您可以修改上一个脚本，以便在用户键入\verb|yes|或\verb|no|以外的任何内容时报告错误消息。请将\verb|else|替换为\verb|elif|，然后添加另一个条件：
\begin{lstlisting}[language=sh]
#!/bin/sh

echo “Is it morning? Please answer yes or no”
read timeofday

if [ $timeofday = “yes” ]
then
    echo “Good morning”
elif [ $timeofday = “no” ]; then
    echo “Good afternoon”
else
    echo “Sorry, $timeofday not recognized. Enter yes or no”
    exit 1
fi
exit 0
    
\end{lstlisting}
这与前面的示例非常相似，但现在如果第一个if条件不为true，elif命令将再次测试变量。如果两个测试都不成功，则会打印一条错误消息，脚本将以值1退出。

\noindent 但这还会导致一些变量的问题，可以通过加引号的方式予以解决

\noindent 新脚本如下：
\begin{lstlisting}[language=sh]
#!/bin/sh

echo "Is it morning? Please answer yes or no"
read timeofday

if [ "$timeofday"= "yes" ]
then
    echo "Good morning"
elif [ "$timeofday" = "no" ]; then
    echo "Good afternoon"
else
    echo "Sorry, $timeofday not recognized. Enter yes or no"
    exit1
fi

exit0
\end{lstlisting}
使用\verb|for|构造可以循环通过一系列值
\noindent 语法很简单：
\begin{lstlisting}[language=sh]
for variable in values
do
    statements
done
\end{lstlisting}
值通常是字符串：
\begin{lstlisting}[language=sh]
#!/bin/sh

for foo in bar fud 43
do
    echo $foo
done
exit 0
\end{lstlisting}
这将产生以下输出：
\begin{lstlisting}[language=sh]
bar
fud
43
\end{lstlisting}
将\verb|for|循环与\verb|shell|扩展一起用于文件名是很常见的，假设您想打印当前目录中以字母“f”开头的所有脚本文件，并且您知道所有脚本都以\verb|.sh|结尾。您可以这样做：
\begin{lstlisting}[language=sh]
#!/bin/sh

for file in $(ls f*.sh); do
    lpr $file
done
exit 0
\end{lstlisting}
shell展开\verb|f*.sh|以给出与此模式匹配的所有文件的名称。\\
\noindent 当您需要重复一系列命令，但不知道它们应该执行多少次时，通常会使用\verb|while|循环，该循环具有以下语法：
\begin{lstlisting}[language=sh]
while condition do
    statements
done
\end{lstlisting}
比如我们以一个简单的密码检查程序作为例子:
\begin{lstlisting}[language=sh]
#!/bin/sh

echo "Enter password"
read trythis

while [ "$trythis" != "secret" ]; do
    echo "Sorry, try again"
    read trythis
done
exit 0
\end{lstlisting}
结果会是:
\begin{lstlisting}[language=sh]
Enter password
password
Sorry, try again
secret
$
\end{lstlisting}
\verb|until|语句具有以下语法
\begin{lstlisting}[language=sh]
until condition
do
    statements
done
\end{lstlisting}
循环会一直持续到条件为真，而不是当条件为真时。\\
\noindent 以下是一个\verb|until|循环的例子:
\begin{lstlisting}[language=sh]
#!/bin/bash

until who | grep “$1” > /dev/null
do
    sleep 60
done

# now ring the bell and announce the expected user.

echo -e ‘\a’  
echo “**** $1 has just logged in ****“

exit 0
\end{lstlisting}
\verb|case|结构则较为复杂
\begin{lstlisting}[language=sh]
case variable in
    pattern [ | pattern] ...) statements;;
    pattern [ | pattern] ...) statements;;
    ...
esac
\end{lstlisting}
\verb|case|构造使您能够以一种非常复杂的方式将变量的内容与模式进行匹配，然后根据匹配的模式允许执行不同的语句
\noindent 下面我们将列举三个示例，每次都改进模式匹配。
\begin{lstlisting}[language=sh]
#!/bin/sh

echo "Is it morning? Please answer yes or no"
read timeofday

case "$timeofday" in
    yes)  echo "Good Morning";;
    no )  echo "Good Afternoon";;
    y  )  echo "Good Morning";;
    n  )  echo "Good Afternoon";;
    *  )  echo "Sorry, answer not recognized";;
esac

exit 0
\end{lstlisting}
前面的\verb|case|构造显然比多\verb|if|语句版本更优雅，但通过将模式放在一起，您可以制作一个更干净的版本：
\begin{lstlisting}[language=sh]
#!/bin/sh

echo "Is it morning? Please answer yes or no"
read timeofday

case "$timeofday" in
    yes | y | Yes | YES )      echo "Good Morning";;
    n* | N* )                  echo "Good Afternoon";;
    * )                        echo "Sorry, answer not recognized";;
esac

exit 0
\end{lstlisting}
最后，为了使脚本可重用，当使用默认模式时，您需要有一个不同的退出值，因为输入不被理解：
\begin{lstlisting}[language=sh]
#!/bin/sh

echo "Is it morning? Please answer yes or no"
read timeofday

case "$timeofday" in
    yes | y | Yes | YES )
        echo "Good Morning"
        echo "Up bright and early this morning"
        ;;
    [nN]*)
        echo "Good Afternoon"
        ;;
    *)
        echo "Sorry, answer not recognized"
        echo "Please answer yes or no"
        exit 1
        ;;
esac

exit 0
\end{lstlisting}
AND列表构造使您能够执行一系列命令，只有在之前的所有命令都成功的情况下才能执行下一个命令。语法为:
\begin{lstlisting}[language=sh]
statement1 && statement2 && statement3 && ...
\end{lstlisting}
每个语句都是独立执行的，使您能够将许多不同的命令混合在一个列表中，如以下脚本所示。如果所有命令都成功执行，则AND列表作为一个整体会成功，但如果不成功，则会失败。

\noindent 在下面的脚本中，您可以触摸\verb|file_one|（检查它是否存在，如果不存在则创建它），然后删除\verb|file_two|。然后，AND列表测试每个文件的存在，并在它们之间回显一些文本。
\begin{lstlisting}[language=sh]
#!/bin/sh

touch file_one
rm -f file_two

if [ -f file_one ] && echo "hello" && [ -f file_two ] && echo "there"
then
    echo "in if"
else
    echo "in else"
fi

exit 0
\end{lstlisting}
结果为:
\begin{lstlisting}[language=sh]
hello
in else
\end{lstlisting}
OR列表构造使我们能够执行一系列命令，直到其中一个成功，然后不再执行。语法如下：
\begin{lstlisting}[language=sh]
statement1 || statement2 || statement3 || ...
\end{lstlisting}
从左边开始，执行每条语句。如果返回\verb|false|，则执行右侧的下一个语句。这种情况一直持续到语句返回\verb|true|，此时不再执行任何语句。

\noindent 我们对上一个示例稍作更改:
\begin{lstlisting}[language=sh]
#!/bin/sh

rm -f file_one

if [ -f file_one ] || echo "hello" || echo " there"
then
    echo "in if"
else
    echo "in else"
fi

exit 0
\end{lstlisting}
结果是:
\begin{lstlisting}[language=sh]
hello
in if
\end{lstlisting}
这两个构造的结果都是要执行的最后一个语句的结果。

\section{函数}
要定义shell函数，只需将其名称后跟空括号，并将语句括在大括号中：
\begin{lstlisting}[language=sh]
function_name () {
    statements
}
\end{lstlisting}
我们先举一个非常简单的函数作为例子:
\begin{lstlisting}[language=sh]
#!/bin/sh

foo() {
    echo "Function foo is executing"
}

echo "script starting"
foo
echo "script ended"

exit 0
\end{lstlisting}
运行脚本将输出以下内容:
\begin{lstlisting}[language=sh]
script starting
Function foo is executing
script ending
\end{lstlisting}
可以使用\verb|return|命令使函数返回数值。\\
\noindent 可以使用local关键字在shell函数中声明局部变量。然后，变量仅在函数的作用域中。否则，函数可以访问其他shell变量，这些变量在作用域上本质上是全局的。如果局部变量与全局变量同名，它会覆盖该变量，但仅在函数内。例如，您可以对前面的脚本进行以下更改，以查看该脚本的运行情况：
\begin{lstlisting}[language=sh]
#!/bin/sh

sample_text="global variable"

foo() {

    local sample_text="local variable"
    echo "Function foo is executing"
    echo $sample_text
}

echo "script starting"
echo $sample_text
foo
echo "script ended"
echo $sample_text

exit 0
\end{lstlisting}
如果没有指定返回值的返回命令，函数将返回最后执行的命令的退出状态。\\
\noindent 下一个脚本\verb|my_name|显示了如何将参数传递给函数，以及函数如何返回\verb|true|或\verb|false|结果。您使用要在问题中使用的名称的参数调用此脚本。

\noindent 1.在shell头之后，定义函数\verb|yes_or_no|：
\begin{lstlisting}[language=sh]
#!/bin/sh

yes_or_no() {
    echo "Is your name $* ?"
    while true
    do
        echo -n "Enter yes or no: "
        read x
        case "$x" in
            y | yes ) return 0;;
            n | no )  return 1;;
            * )       echo "Answer yes or no"
        esac
    done
}
\end{lstlisting}
2.然后程序的主要部分开始：
\begin{lstlisting}[language=sh]
echo "Original parameters are $*"

if yes_or_no "$1"
then
    echo "Hi $1, nice name"
else
    echo "Never mind"
fi
exit 0
\end{lstlisting}
此脚本的典型输出可能如下：
\begin{lstlisting}[language=sh]
$ ./my_name Rick Neil
Original parameters are Rick Neil
Is your name Rick ?
Enter yes or no: yes
Hi Rick, nice name
$
\end{lstlisting}
\section{指令}
在这里，我们将只介绍在编写脚本时使用的主要命令，包括内部和外部命令。\\
在满足控制条件之前，使用break从封闭的for、while或until循环中转义。
\begin{lstlisting}[language=sh]
#!/bin/sh

rm -rf fred*
echo > fred1
echo > fred2
mkdir fred3
echo > fred4

for file in fred*
do
    if [ -d “$file” ]; then
        break;
    fi
done

echo first directory starting fred was $file

rm -rf fred*
exit 0
\end{lstlisting}
冒号命令是一个null命令。作为\verb|true|的别名，简化条件的逻辑有时会很有用。
\begin{lstlisting}[language=sh]
#!/bin/sh

rm -f fred
if [ -f fred ]; then
    :
else
    echo file fred did not exist
fi

exit 0
\end{lstlisting}
\verb|continue|使\verb|for、while|或\verb|until|循环在下一次迭代中继续，循环变量取列表中的下一个值：
\begin{lstlisting}[language=sh]
#!/bin/sh

rm -rf fred*
echo > fred1
echo > fred2
mkdir fred3
echo > fred4

for file in fred*
do
    if [ -d “$file” ]; then
        echo “skipping directory $file”
        continue
    fi
    echo file is $file
done

rm -rf fred*
exit 0
\end{lstlisting}
dot（.）命令在当前shell中执行以下命令：
\begin{lstlisting}[language=sh]
. ./shell_script
\end{lstlisting}
在shell脚本中，dot命令的工作方式有点像\verb|C|或\verb|C++|中的\verb|#include|指令。虽然它没有真正包含脚本，但它确实在当前上下文中执行命令，因此您可以使用它将变量和函数定义合并到脚本中\\
\noindent 以下示例在命令行中使用\verb|dot|命令，但您也可以在脚本中使用它：\\
\noindent 1.假设您有两个文件，其中包含两个不同开发环境的环境设置。要为旧的经典命令\verb|classic_set|设置环境，可以使用以下命令：
\begin{lstlisting}[language=sh]
#!/bin/sh

version=classic
PATH=/usr/local/old_bin:/usr/bin:/bin:.
PS1=" classic> "
\end{lstlisting}
2.对于新命令，请使用\verb|latest_set|：
\begin{lstlisting}[language=sh]
#!/bin/sh

version=latest
PATH=/usr/local/new_bin:/usr/bin:/bin:.
PS1=" latest version> "
\end{lstlisting}
使用\verb|echo|命令可以输出一个后面跟着换行符的字符串，一个常见的问题是如何抑制换行符。Linux中常见的方法是使用:
\begin{lstlisting}[language=sh]
echo -n "string to output"
\end{lstlisting}
但事实上你会经常遇到:
\begin{lstlisting}[language=sh]
echo -e "string to output\c"
\end{lstlisting}
第二个选项\verb|echo-e|确保启用反斜杠转义符的解释，例如\verb|\c|用于抑制换行符、\verb|\t|用于输出制表符和\verb|\n|用于输出回车符 \\
\noindent eval命令使您能够计算参数,它内置在\verb|shell|中。
\begin{lstlisting}[language=sh]
foo=10
x=foo
y='$'$x
echo $y
\end{lstlisting}
这给输出\verb|$foo|,然而
\begin{lstlisting}[language=sh]
foo=10
x=foo
eval y='$'$x
echo $y
\end{lstlisting}
给出输出10。因此，\verb|eval|有点像额外的\verb|$|：它为您提供变量值的值。

\noindent \verb|exec|命令有两种不同的用途。它的典型用途是用不同的程序替换当前shell。例如
\begin{lstlisting}[language=sh]
exec wall "Thanks for all the fish"
\end{lstlisting}
在脚本中，将用\verb|wall|命令替换当前shell。\verb|exec|之后的脚本中没有任何行将被处理，因为执行脚本的shell已不存在。

\noindent \verb|exec|的第二个用途是修改当前的文件描述符：
\begin{lstlisting}[language=sh]
exec 3< afile
\end{lstlisting}
但这种用途很少被使用\\
\noindent exit命令会导致脚本退出，退出代码为n。在shell脚本编程中，退出代码0是成功的，代码1到125（包括125）是脚本可以使用的错误代码。其余值具有保留含义，如下表所示：\\
\begin{table}[hp]
\centering
\begin{tabular}{|c|c|}
\hline
退出代码 & 含义 \\
\hline
126 & 文件不可执行 \\
\hline
127 & 指令未找到  \\
\hline
128及以上 & 信号出现 \\
\hline
\end{tabular}
\end{table}

\noindent 脚本的最大优点是，它们使您能够使用125个用户定义的错误代码，而不需要全局错误代码变量。\\
\noindent 以下是一个简单的示例，如果当前目录中存在名为\verb|.profile|的文件，则返回成功：
\begin{lstlisting}[language=sh]
#!/bin/sh

if [ -f .profile ]; then
    exit 0
fi

exit 1
\end{lstlisting}
也可以简洁地表示为:
\begin{lstlisting}[language=sh]
[ -f .profile ] && exit 0 || exit 1
\end{lstlisting}
\verb|export|命令使作为其参数命名的变量在子shell中可用，以下我们举两个例子说明这一点:
\noindent 1.首先，列出\verb|export2|
\begin{lstlisting}[language=sh]
#!/bin/sh

echo "$foo"
echo "$bar"
\end{lstlisting}
\noindent 2.现在轮到\verb|export1|，在该脚本结束时，调用\verb|export2|
\begin{lstlisting}[language=sh]
#!/bin/sh

foo=”The first meta-syntactic variable”
export bar=”The second meta-syntactic variable”

export2
\end{lstlisting}
如果你运行这些，你将得到:
\begin{lstlisting}[language=sh]
$ ./export1

The second meta-syntactic variable
$
\end{lstlisting}
一旦变量从shell导出，它就会导出到从该shell调用的任何脚本，以及它们依次调用的任何shell，依此类推。


\bibliographystyle{plain}
\bibliography{crazyfish.bib}

\end{document}
